﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Webcam.Status
{
    /// <summary>
    /// The time taken for this object
    /// </summary>
    public class TimeTaken
    {
        public static string FormString ()
        {
            StringBuilder s = new StringBuilder();
            Root.formString(s);
            return s.ToString();
        }

        private StringBuilder formString(StringBuilder s)
        {
            int d = Depth;
            for (int i = 0; i < d; i++)
                s.Append(" ");

            s.Append(this.Name + " (" + this.Time + "ms)\n");
            foreach (TimeTaken tt in Children)
                tt.formString(s);

            return s;
        }

        /// <summary>
        /// The Root object
        /// </summary>
        public static TimeTaken Root = new TimeTaken();

        /// <summary>
        /// The child elements of this object
        /// </summary>
        public List<TimeTaken> Children = new List<TimeTaken>();

        /// <summary>
        /// Root constructor
        /// </summary>
        private TimeTaken()
        {

        }

        /// <summary>
        /// Add a new object to the collection
        /// </summary>
        /// <param name="Name"></param>
        /// <param name="Parent"></param>
        private TimeTaken(string Name, string Parent)
        {
            this.p_Parent = Root.SubElement(Parent);
            if (this.p_Parent != null)
            {
                // register with parent
                this.p_Parent.Children.Add(this);
            }
            else
            {
                // register with root
                Root.Children.Add(this);
            }
            this.Name = Name;
        }



        /// <summary>
        /// Find a sub element of this child
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public TimeTaken SubElement(string name)
        {
            if (name == string.Empty)
                return Root;
            foreach (TimeTaken c in this.Children)
            {
                if (c.Name == name)
                {
                    return c;
                }

                TimeTaken childElement = c.SubElement(name);
                if (childElement != null)
                    return childElement;
            }
            return null;
        }

        /// <summary>
        /// Time taken objects name
        /// </summary>
        public string Name = string.Empty;

        /// <summary>
        /// Does this objet contain a child element of name 'name'
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool Exists(string name)
        {
            if (this.Name == string.Empty && name == string.Empty)
                return true;

            foreach (TimeTaken c in Children)
            {
                if (c.Name == name)
                {
                    return true;
                }

                if (c.Exists(name))
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Update an object
        /// </summary>
        /// <param name="name"></param>
        /// <param name="number"></param>
        public static void Update(string name, long time)
        {
            TimeTaken obj = Root.SubElement(name);
            if (obj == null)
            {
                // name is not in set already, so add it
                // add to root as no other position provided
                TimeTaken tt = new TimeTaken(name, "");
                tt.p_time = time;
            }
            else
            {
                // update it
                obj.p_time = time;
            }
        }

        /// <summary>
        /// Add an object to the time taken collection with the specified parent
        /// </summary>
        /// <param name="name"></param>
        /// <param name="parent"></param>
        /// <returns></returns>
        public static TimeTaken Add(string name, string parent)
        {
            TimeTaken tt = new TimeTaken(name, parent);
            return tt;
        }


        /// <summary>
        /// The time taken's parent
        /// </summary>
        private TimeTaken p_Parent = null;

        /// <summary>
        /// The time taken's parent
        /// </summary>
        public TimeTaken Parent
        {
            get { return this.p_Parent; }
        }


        /// <summary>
        /// How deep is this node?
        /// </summary>
        public int Depth
        {
            get { return DepthOfNode(this); }
        }

        /// <summary>
        /// How deep is this node?
        /// </summary>
        /// <param name="tt"></param>
        /// <returns></returns>
        public static int DepthOfNode(TimeTaken tt)
        {
            int c = 0;
            while (tt.Parent != null)
            {
                c++;
                tt = tt.Parent;
            }
            return c;
        }

        
        /// <summary>
        /// This node's time
        /// </summary>
        private long p_time = 0;

        /// <summary>
        /// The total time for this node
        /// </summary>
        public long Time
        {
            get
            {
                long t = p_time;
                foreach (TimeTaken tt in Children)
                {
                    t += tt.Time;
                }
                return t;
            }
        }
    }
}
